package Interface 
{
	import flash.display.BitmapData;
	import flash.geom.Rectangle;
	import org.flixel.FlxG;
	import org.flixel.FlxTilemap;
	import org.flixel.FlxU;
	
	/**
	 * ...
	 * @author Gregory Weir
	 */
	public class WorldTilemap extends FlxTilemap
	{
		[Embed(source = '../../graphics/uberMapTiles.png')]
		private static var SprMapTiles:Class;
		
		private static const TILE_SIZE:uint = 32;
		private static const JUNK_INDEX:uint = 7;
		private static const TILESET_TILEWIDTH:uint = 35;
		private static const TRANSITION_SET_WIDTH:uint = 5; //in tile units, not pixels
		private static const TRANSITION_SET_HEIGHT:uint = 3; //in tile units, not pixels
		
		private var mapData:Array;
		private var overlayData:Array;
		
		public function WorldTilemap(world:World) 
		{
			calcMapData(world);
			drawIndex = 0;
			loadWorldMap(mapData, SprMapTiles);
			
			trace(overlayData);
		}
		
		private function calcMapData(world:World):Array
		{
			mapData = new Array();
			overlayData = new Array();
			
			for (var i:int = 0; i < world.getWidth(); i++)
			{
				mapData[2*i] = new Array();
				mapData[2 * i + 1] = new Array();
				
				overlayData[2*i] = new Array();
				overlayData[2*i + 1] = new Array();
				
				for (var j:int = 0; j < world.getHeight(); j++)
				{
					mapData[2 * i][2 * j] = world.getBiome(i, j);
				}
			}
			
			for (i = 0; i < mapData.length - 2; i+=2)
			{
				for (j = 0; j < mapData[0].length - 2; j+=2)
				{
					if (mapData[i][j] == mapData[i + 2][j]) mapData[i + 1][j] = mapData[i][j];
					else mapData[i + 1][j] = getHTransitionTile(mapData[i][j], mapData[i + 2][j]);
					
					if (mapData[i][j] == mapData[i][j + 2]) mapData[i][j + 1] = mapData[i][j];
					else mapData[i][j + 1] = getVTransitionTile(mapData[i][j], mapData[i][j + 2]);
					
					mapData[i + 1][j + 1] = getDTransitionTile(i + 1, j + 1);
				}
			}
			
			return mapData;
		}
		
		private function getDTransitionTile(i:int, j:int):int
		{
			var UL:int = mapData[i - 1][j - 1];
			var UR:int = mapData[i + 1][j - 1];
			var LL:int = mapData[i - 1][j + 1];
			var LR:int = mapData[i + 1][j + 1];
			
			return getDTransitionTileFromCorners(UL, UR, LL, LR, i, j);
		}
		
		private function getDTransitionTileFromCorners(UL:int, UR:int, LL:int, LR:int, i:int, j:int):int
		{
			if (UL == UR && UL == LL && UL == LR) return UL;
			else if (UL == LL && UR == LR) return getHTransitionTile(UL, UR);
			else if (UL == UR && LL == LR) return getVTransitionTile(UL, LL);
			else if (UL == UR && UL == LL) return getTransitionTile(UL, LR, 0, 0, 2, 0);
			else if (UL == UR && UR == LR) return getTransitionTile(UL, LL, 1, 0, 3, 0);
			else if (UL == LL && LL == LR) return getTransitionTile(UL, UR, 0, 2, 2, 1);
			else if (UR == LR && UR == LL) return getTransitionTile(LR, UL, 1, 2, 3, 1);
			else if (UL == LR && UR == LL) return getTransitionTile(UL, UR, 4, 1, 4, 0);
			else
			{
				if (UL != UR && UL != LR && UL != LL && UR != LR && UR != LL && LL != LR) trace("Wow, a 4-way!  Shit!");
				else
				{
					var lowest:int = UL;
					var secondLowest:int = 100;
					
					if (UR < lowest) lowest = UR;
					if (LR < lowest) lowest = LR;
					if (LL < lowest) lowest = LL;
					
					if (UL < secondLowest && UL > lowest) secondLowest = UL;
					if (UR < secondLowest && UR > lowest) secondLowest = UR;
					if (LR < secondLowest && LR > lowest) secondLowest = LR;
					if (LL < secondLowest && LL > lowest) secondLowest = LL;
					
					if (UL > secondLowest && UR > secondLowest) overlayData[i][j] = (UL * TRANSITION_SET_WIDTH) + 2 + (2 * TILESET_TILEWIDTH);
					else if (LL > secondLowest && LR > secondLowest) overlayData[i][j] = (LL * TRANSITION_SET_WIDTH) + 3 + (2 * TILESET_TILEWIDTH);
					else if (UL > secondLowest && LL > secondLowest) overlayData[i][j] = (UL * TRANSITION_SET_WIDTH) + 1 + (1 * TILESET_TILEWIDTH);
					else if (UR > secondLowest && LR > secondLowest) overlayData[i][j] = (UR * TRANSITION_SET_WIDTH) + 0 + (1 * TILESET_TILEWIDTH);
					else if (UL > secondLowest) overlayData[i][j] = (UL * TRANSITION_SET_WIDTH) + 1 + (2 * TILESET_TILEWIDTH);
					else if (UR > secondLowest) overlayData[i][j] = (UR * TRANSITION_SET_WIDTH) + 0 + (2 * TILESET_TILEWIDTH);
					else if (LL > secondLowest) overlayData[i][j] = (LL * TRANSITION_SET_WIDTH) + 1;
					else if (LR > secondLowest) overlayData[i][j] = (LR * TRANSITION_SET_WIDTH);
					
					
					return getDTransitionTileFromCorners(UL > secondLowest ? secondLowest : UL,
													UR > secondLowest ? secondLowest : UR,
													LL > secondLowest ? secondLowest : LL,
													LR > secondLowest ? secondLowest : LR, i, j);
				}
			}
			
			return JUNK_INDEX;
		}
		
		private function getHTransitionTile(left:int, right:int):int
		{
			return getTransitionTile(left, right, 0, 1, 1, 1);
			//var xOffset:uint = left < right ? right * TRANSITION_SET_WIDTH : left * TRANSITION_SET_WIDTH;
			//var yOffset:uint = left < right ? TRANSITION_SET_HEIGHT + left * TRANSITION_SET_HEIGHT : TRANSITION_SET_HEIGHT + right * TRANSITION_SET_HEIGHT;
			//
			//if (left < right) return TILESET_TILEWIDTH * (yOffset + 1) + xOffset;
			//else return TILESET_TILEWIDTH * (yOffset + 1) + xOffset + 1;
		}
		
		private function getVTransitionTile(top:int, bottom:int):int
		{
			return getTransitionTile(top, bottom, 3, 2, 2, 2);
			//var xOffset:uint = top < bottom ? bottom * TRANSITION_SET_WIDTH : top * TRANSITION_SET_WIDTH;
			//var yOffset:uint = top < bottom ? TRANSITION_SET_HEIGHT + top * TRANSITION_SET_HEIGHT : TRANSITION_SET_HEIGHT + bottom * TRANSITION_SET_HEIGHT;
			//
			//if (top < bottom) return TILESET_TILEWIDTH * (yOffset + 2) + xOffset + 3;
			//else return TILESET_TILEWIDTH * (yOffset + 2) + xOffset + 2;
		}
		
		/**
		 * Gets the appropriate transition tile for two adjacent biomes, given their indexes and the positions of the appropriate tiles in a transitional tileset.
		 * @param	first The first biome.
		 * @param	second The second biome.
		 * @param	lessX The x index of the tile if the first biome is "beneath" the second.
		 * @param	lessY The y index of the tile if the first biome is "beneath" the second.
		 * @param	greatX The x index of the tile if the first biome is "above" the second.
		 * @param	greatY The y index of the tile if the first biome is "above" the second.
		 * @return
		 */
		private function getTransitionTile(first:int, second:int, lessX:int, lessY:int, greatX:int, greatY:int):int
		{
			var xOffset:uint = first < second ? second * TRANSITION_SET_WIDTH : first * TRANSITION_SET_WIDTH;
			var yOffset:uint = first < second ? TRANSITION_SET_HEIGHT + first * TRANSITION_SET_HEIGHT : TRANSITION_SET_HEIGHT + second * TRANSITION_SET_HEIGHT;
			
			if (first < second) return xOffset + lessX + TILESET_TILEWIDTH * (yOffset + lessY);
			else return xOffset + greatX + TILESET_TILEWIDTH * (yOffset + greatY);
		}
		
		public function loadWorldMap(MapData:Array, TileGraphic:Class):FlxTilemap
		{
			refresh = true;
			
			//Figure out the map dimensions based on the data string
			widthInTiles = MapData.length;
			heightInTiles = MapData[0].length;
			
			var i:uint;
			var j:uint;
			totalTiles = widthInTiles*heightInTiles;
			
			var value:*;
			_data = new Array();
			for (j = 0; j < heightInTiles; j++)
			{
				for (i = 0; i < widthInTiles; i++)
				{
					value = MapData[i][j];
					if (value is int) _data.push(value);
					else _data.push(JUNK_INDEX);
				}
			}
			
			//Figure out the size of the tiles
			_pixels = FlxG.addBitmap(TileGraphic);
			_tileWidth = TILE_SIZE;
			_tileHeight = TILE_SIZE;
			
			_block.width = _tileWidth;
			_block.height = _tileHeight;
			
			//Then go through and create the actual map
			width = widthInTiles*_tileWidth;
			height = heightInTiles*_tileHeight;
			_rects = new Array(totalTiles);
			i = 0;
			while(i < totalTiles)
				updateTile(i++);
			
			//Also need to allocate a buffer to hold the rendered tiles
			var bw:uint = (FlxU.ceil(FlxG.width / _tileWidth) + 1)*_tileWidth;
			var bh:uint = (FlxU.ceil(FlxG.height / _tileHeight) + 1)*_tileHeight;
			_buffer = new BitmapData(bw,bh,true,0);
			
			//Pre-set some helper variables for later
			_screenRows = Math.ceil(FlxG.height/_tileHeight)+1;
			if(_screenRows > heightInTiles)
				_screenRows = heightInTiles;
			_screenCols = Math.ceil(FlxG.width/_tileWidth)+1;
			if(_screenCols > widthInTiles)
				_screenCols = widthInTiles;
			
			_bbKey = String(TileGraphic);
			generateBoundingTiles();
			refreshHulls();
			
			_flashRect.x = 0;
			_flashRect.y = 0;
			_flashRect.width = _buffer.width;
			_flashRect.height = _buffer.height;
			
			return this;
		}
		
		override protected function renderTilemap():void 
		{
			super.renderTilemap();
			
			//Copy tile images into the tile buffer
			getScreenXY(_point);
			_flashPoint.x = _point.x;
			_flashPoint.y = _point.y;
			var tx:int = Math.floor(-_flashPoint.x/_tileWidth);
			var ty:int = Math.floor( -_flashPoint.y / _tileHeight);
			
			_flashPoint.x = 0;
			
			for (var i:int = tx; i < tx + _screenCols; i++ )
			{
				
				if (i < 0) i = 0;
				if (i > widthInTiles) break;
				
				_flashPoint.y = 0;
				
				for (var j:int = ty; j < ty + _screenRows; j++)
				{
					if (j < 0) j = 0;
					if (j > heightInTiles) break;
					if (overlayData[i][j] != null)
					{
						_flashRect = new Rectangle((overlayData[i][j] % TILESET_TILEWIDTH) * _tileWidth, uint(overlayData[i][j] / TILESET_TILEWIDTH) * _tileHeight, _tileWidth, _tileHeight);
						_buffer.copyPixels(_pixels, _flashRect, _flashPoint, null, null, true);
					}
					
					_flashPoint.y += _tileHeight;
				}
				
				_flashPoint.x += _tileWidth;
			}
			
			//if(tx < 0) tx = 0;
			//if(tx > widthInTiles-_screenCols) tx = widthInTiles-_screenCols;
			//if(ty < 0) ty = 0;
			//if(ty > heightInTiles-_screenRows) ty = heightInTiles-_screenRows;
			//var ri:int = ty*widthInTiles+tx;
			//_flashPoint.y = 0;
			//var r:uint = 0;
			//var c:uint;
			//var cri:uint;
			//while(r < _screenRows)
			//{
				//cri = ri;
				//c = 0;
				//_flashPoint.x = 0;
				//while(c < _screenCols)
				//{
					//_flashRect = _rects[cri++] as Rectangle;
					//if(_flashRect != null)
						//_buffer.copyPixels(tileBitmap,_flashRect,_flashPoint,null,null,true);
					//_flashPoint.x += _tileWidth;
					//c++;
				//}
				//ri += widthInTiles;
				//_flashPoint.y += _tileHeight;
				//r++;
			//}
			_flashRect = _flashRect2;
			//_bufferLoc.x = tx*_tileWidth;
			//_bufferLoc.y = ty*_tileHeight;
		}
	}

}